package com.bjpowernode.filter;

import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import cn.hutool.jwt.JWTUtil;
import com.bjpowernode.constant.Constant;
import com.bjpowernode.entity.TUser;
import com.bjpowernode.result.Result;
import jakarta.annotation.Resource;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.filter.OncePerRequestFilter;

import java.io.IOException;
import java.util.concurrent.TimeUnit;

/**
 * JWT令牌验证过滤器
 * 用于拦截并验证请求中的JWT令牌，实现基于令牌的身份认证与授权
 */
@Component
public class TokenFilter extends OncePerRequestFilter {
    @Resource
    private RedisTemplate redisTemplate; // Redis操作模板，用于存取和验证令牌

    /**
     * 核心过滤方法，处理每个请求的令牌验证逻辑
     */
    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        // 设置响应格式为JSON，字符编码为UTF-8，确保前端能正确解析响应内容
        response.setContentType("application/json");
        response.setCharacterEncoding("utf-8");

        // 获取请求URI，用于判断是否为登录接口
        String requestUri = request.getRequestURI();
        // 登录接口无需验证令牌（登录时还未生成令牌），直接放行
        if (requestUri.equals("/api/login")) {
            filterChain.doFilter(request, response);
        } else {
            // 从请求头获取Authorization字段（令牌通常放在该位置）
            String token = request.getHeader("Authorization");
            // 令牌为空校验：若请求头中无令牌，返回901错误
            if (!StringUtils.hasText(token)) {
                Result result = Result.builder().code(901).msg("请求Token为空").build();
                response.getWriter().write(JSONUtil.toJsonStr(result));
            } else {
                boolean verify = false; // 令牌验证结果标志（默认未通过）
                try {
                    // 使用JWT工具验证令牌签名（基于系统密钥SECRET）
                    verify = JWTUtil.verify(token, Constant.SECRET.getBytes());
                } catch (Exception e) {
                    e.printStackTrace(); // 捕获验证异常（如签名错误、令牌过期等）
                }
                // 令牌不合法校验：验证失败返回902错误
                if (!verify) {
                    Result result = Result.builder().code(902).msg("请求Token不合法").build();
                    response.getWriter().write(JSONUtil.toJsonStr(result));
                } else {
                    // 解析令牌载荷（Payload），获取存储的用户信息JSON字符串
                    JSONObject payloads = JWTUtil.parseToken(token).getPayloads();
                    String userJSON = payloads.get("user", String.class);
                    // 将用户JSON字符串转换为TUser实体对象
                    TUser tUser = JSONUtil.toBean(userJSON, TUser.class);
                    Integer userId = tUser.getId(); // 获取用户ID，用于Redis令牌查询

                    // 从Redis获取该用户存储的有效令牌（防止令牌篡改或过期）
                    String redisToken = (String) redisTemplate.opsForValue().get(Constant.REDIS_TOKEN_KEY + userId);
                    // 令牌一致性校验：请求令牌与Redis存储令牌不一致时返回903错误
                    if (!token.equals(redisToken)) {
                        Result result = Result.builder().code(903).msg("请求Token错误").build();
                        response.getWriter().write(JSONUtil.toJsonStr(result));
                    } else {
                        // 令牌验证通过：构建Spring Security认证对象
                        // 将用户信息、凭证（null）和权限集合存入认证令牌
                        UsernamePasswordAuthenticationToken authenticationToken
                                = new UsernamePasswordAuthenticationToken(tUser, null, tUser.getAuthorities());
                        // 将认证对象存入安全上下文，供后续权限校验使用
                        SecurityContextHolder.getContext().setAuthentication(authenticationToken);

                        // 刷新Redis令牌过期时间（30分钟），实现"活跃用户会话延长"机制
                        redisTemplate.expire(Constant.REDIS_TOKEN_KEY + userId, 30L, TimeUnit.MINUTES);

                        // 令牌验证通过，放行请求至后续过滤器或控制器
                        filterChain.doFilter(request, response);
                    }
                }
            }
        }
    }
}